﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Elderos.Ontology;
using Elderos.Utils.Logging;

namespace Elderos.Highlight.Search
{
    public abstract class TextSearcherBase : ISearchStrategy
    {
        private readonly ReaderWriterLock _locker = new ReaderWriterLock();

        public bool IsInitialized { get; private set; }

        public event Action BeforeIntitialization;

        private void InvokeBeginInitialize()
        {
            Action handler = BeforeIntitialization;
            if (handler != null) handler();
        }

        public event Action AfterInitialization;

        private void InvokeAfterInitialization()
        {
            Action handler = AfterInitialization;
            if (handler != null) handler();
        }

        private ISynonymSource _synonymSource;

        protected TextSearcherBase(ISynonymSource synonymSource)
        {
            Logger.Assert(() => synonymSource != null, "Synonym source can't be null");
            _synonymSource = synonymSource;
        }

        public void Initialize()
        {
            InvokeBeginInitialize();
            try
            {
                _locker.AcquireWriterLock(-1);

                LoadSynonyms();

                IsInitialized = true;
            }
            finally
            {
                _locker.ReleaseWriterLock();
            }
            InvokeAfterInitialization();
        }

        private void LoadSynonyms()
        {
            int synonymsCount = 1;
            
            if (InitializationProgress != null)
               synonymsCount = GetSynonymCount();

            int counter = 0;

            Synonym synonym = null;

            foreach (var synoDTO in _synonymSource.LoadSynonyms())
            {
                int id = synoDTO.SynonymID;
                string syno = synoDTO.Synonym; // String.Intern(synoDTO.Synonym);
                int entityID = synoDTO.EntityID;

                if (synonym == null || synonym.ID != id)
                {
                    synonym = new Synonym(id, syno);
                    ProcessSynonym(synonym);
                }

                synonym.EntityIDs.Add(entityID);

                counter++;
                if (counter % 100000 == 0)
                {
                    OnInitializationProgress(counter * 1.0 / synonymsCount, counter, synonymsCount);
                }
            }
        }

        protected abstract void ProcessSynonym(Synonym synonym);

        public List<Position> Search(PreparedText text)
        {
            if (!IsInitialized)
                throw new InvalidOperationException("Searcher is not initialized.");
            return InnerSearch(text);
        }

        private int GetSynonymCount()
        {
            return _synonymSource.EstimatedSynonymCount;
        }

        public event InitializationProgressHandler InitializationProgress;

        private void OnInitializationProgress(double obj, int count, int total)
        {
            InitializationProgressHandler handler = InitializationProgress;
            if (handler != null) handler(obj, count, total);
        }

        protected abstract List<Position> InnerSearch(PreparedText text);

        protected Synonym GetMainSyno(Synonym first, Synonym second)
        {
            int firstUpper = first.Syno.Count(char.IsUpper);
            int secondUpper = second.Syno.Count(char.IsUpper);

            if (firstUpper > secondUpper) return second;
            return first;
        }

        protected static bool AsymmetricEquals(string synonym, string pretender)
        {
            if (synonym == null && pretender != null) return false;
            if (synonym != null && pretender == null) return false;
            if (synonym == null && pretender == null) return true;

            if (synonym.Length != pretender.Length) return false;

            for (int i = 0; i < synonym.Length; i++)
            {
                char s = synonym[i];
                char p = pretender[i];

                if (s == p) continue;
                if (char.ToUpper(s) == p) continue;
                return false;
            }

            return true;
        }
    }
}
